﻿/*
   Copyright 2012 Rajat Goyal - gyl.rajat@gmail.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace KineSis.Speech
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using Microsoft.Kinect;
    using Microsoft.Speech.AudioFormat;
    using Microsoft.Speech.Recognition;
    

    public class SpeechRecognizer : IDisposable
    {
        private readonly Dictionary<string, WhatSaid> singlePhrases = new Dictionary<string, WhatSaid>
            {
                { "Next", new WhatSaid { Verb = Verbs.Next } },
                { "Previous", new WhatSaid { Verb = Verbs.Previous } },
                { "Next Page", new WhatSaid { Verb = Verbs.Next } },
                { "Previous Page", new WhatSaid { Verb = Verbs.Previous } },
                { "Bigger", new WhatSaid { Verb = Verbs.Bigger } },
                { "Big", new WhatSaid { Verb = Verbs.Bigger } },
                { "Larger", new WhatSaid { Verb = Verbs.Bigger } },
                { "Huge", new WhatSaid { Verb = Verbs.Biggest } },
                { "Giant", new WhatSaid { Verb = Verbs.Biggest } },
                { "Biggest", new WhatSaid { Verb = Verbs.Biggest } },
                { "Smaller", new WhatSaid { Verb = Verbs.Smaller } },
                { "Tiny", new WhatSaid { Verb = Verbs.Smallest } },
                { "Super Small", new WhatSaid { Verb = Verbs.Smallest } },
                { "Smallest", new WhatSaid { Verb = Verbs.Smallest } },
                { "Read", new WhatSaid {Verb = Verbs.Read } },
                { "right", new WhatSaid { Verb = Verbs.right } },
                { "left", new WhatSaid {Verb = Verbs.left } },
                { "up", new WhatSaid { Verb = Verbs.up} },
                { "down", new WhatSaid {Verb = Verbs.down } },
                { "fit", new WhatSaid {Verb = Verbs.down } },
                { "Reset", new WhatSaid { Verb = Verbs.Reset } },
                { "Clear", new WhatSaid { Verb = Verbs.Reset } },
                { "Stop", new WhatSaid { Verb = Verbs.Pause } },
                { "Pause Game", new WhatSaid { Verb = Verbs.Pause } },
                { "Freeze", new WhatSaid { Verb = Verbs.Pause } },
                { "Unfreeze", new WhatSaid { Verb = Verbs.Resume } },
                { "Resume", new WhatSaid { Verb = Verbs.Resume } },
                { "Continue", new WhatSaid { Verb = Verbs.Resume } },
                { "Play", new WhatSaid { Verb = Verbs.Resume } },
                { "Start", new WhatSaid { Verb = Verbs.Resume } },
                { "Go", new WhatSaid { Verb = Verbs.Resume } },
            };

      

        private SpeechRecognitionEngine sre;
        private KinectAudioSource kinectAudioSource;
        private bool paused;
        private bool isDisposed;

        private SpeechRecognizer()
        {
            RecognizerInfo ri = GetKinectRecognizer();
            this.sre = new SpeechRecognitionEngine(ri);
            this.LoadGrammar(this.sre);
        }

        public event EventHandler<SaidSomethingEventArgs> SaidSomething;

        public enum Verbs
        {
            None = 0,
            Bigger,
            Biggest,
            Smaller,
            Smallest,
            Next,
            Previous,
            Read,
            Reset,
            Pause,
            Resume,
            right,
            left,
            up,
            down,
            fit
        }

        public EchoCancellationMode EchoCancellationMode
        {
            get
            {
                this.CheckDisposed();

                return this.kinectAudioSource.EchoCancellationMode;
            }

            set
            {
                this.CheckDisposed();

                this.kinectAudioSource.EchoCancellationMode = value;
            }
        }

   
        public static SpeechRecognizer Create()
        {
            SpeechRecognizer recognizer = null;

            try
            {
                recognizer = new SpeechRecognizer();
            }
            catch (Exception)
            {
               
            }

            return recognizer;
        }



        public void Start(KinectAudioSource kinectSource)
        {
            this.CheckDisposed();

            this.kinectAudioSource = kinectSource;
            this.kinectAudioSource.AutomaticGainControlEnabled = false;
            this.kinectAudioSource.BeamAngleMode = BeamAngleMode.Adaptive;
            var kinectStream = this.kinectAudioSource.Start();
            this.sre.SetInputToAudioStream(
                kinectStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
            this.sre.RecognizeAsync(RecognizeMode.Multiple);
        }

        public void Stop()
        {
            this.CheckDisposed();

            if (this.sre != null)
            {
                this.kinectAudioSource.Stop();
                this.sre.RecognizeAsyncCancel();
                this.sre.RecognizeAsyncStop();

                this.sre.SpeechRecognized -= this.SreSpeechRecognized;
                this.sre.SpeechHypothesized -= this.SreSpeechHypothesized;
                this.sre.SpeechRecognitionRejected -= this.SreSpeechRecognitionRejected;
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "sre",
            Justification = "This is suppressed because FXCop does not see our threaded dispose.")]
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.Stop();

                if (this.sre != null)
                {
                    // NOTE: The SpeechRecognitionEngine can take a long time to dispose
                    // so we will dispose it on a background thread
                    ThreadPool.QueueUserWorkItem(
                        delegate(object state)
                        {
                            IDisposable toDispose = state as IDisposable;
                            if (toDispose != null)
                            {
                                toDispose.Dispose();
                            }
                        },
                            this.sre);
                    this.sre = null;
                }

                this.isDisposed = true;
            }
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            Func<RecognizerInfo, bool> matchingFunc = r =>
            {
                string value;
                r.AdditionalInfo.TryGetValue("Kinect", out value);
                return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
            };
            return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
        }

        private void CheckDisposed()
        {
            if (this.isDisposed)
            {
                throw new ObjectDisposedException("SpeechRecognizer");
            }
        }

        private void LoadGrammar(SpeechRecognitionEngine speechRecognitionEngine)
        {
           
            var single = new Choices();
            foreach (var phrase in this.singlePhrases)
            {
                single.Add(phrase.Key);
            }
                        
            var actionGrammar = new GrammarBuilder();
            actionGrammar.AppendWildcard();
            actionGrammar.Append(single);

            var allChoices = new Choices();
            allChoices.Add(actionGrammar);
            allChoices.Add(single);

           
            var gb = new GrammarBuilder { Culture = speechRecognitionEngine.RecognizerInfo.Culture };
            gb.Append(allChoices);
            
            var g = new Grammar(gb);
            speechRecognitionEngine.LoadGrammar(g);
            speechRecognitionEngine.SpeechRecognized += this.SreSpeechRecognized;
            speechRecognitionEngine.SpeechHypothesized += this.SreSpeechHypothesized;
            speechRecognitionEngine.SpeechRecognitionRejected += this.SreSpeechRecognitionRejected;
        }

        private void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            var said = new SaidSomethingEventArgs { Verb = Verbs.None, Matched = "?" };

            if (this.SaidSomething != null)
            {
                this.SaidSomething(new object(), said);
            }

            Console.WriteLine("\nSpeech Rejected");
        }

        private void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            Console.Write("\rSpeech Hypothesized: \t{0}", e.Result.Text);
        }

        private void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            Console.Write("\rSpeech Recognized: \t{0}", e.Result.Text);

            if ((this.SaidSomething == null) || (e.Result.Confidence < 0.3))
            {
                return;
            }

            var said = new SaidSomethingEventArgs { Verb = 0, Phrase = e.Result.Text };

      
       
            List<Dictionary<string, WhatSaid>> allDicts = new List<Dictionary<string, WhatSaid>> { this.singlePhrases };

            bool found = false;
            for (int i = 0; i < allDicts.Count && !found; ++i)
            {
                foreach (var phrase in allDicts[i])
                {
                    if (e.Result.Text.Contains(phrase.Key))
                    {
                        said.Verb = phrase.Value.Verb;
                        said.Matched = phrase.Key;
                        found = true;
                        break;
                    }
                }
            }

            if (!found)
            {
                return;
            }

            if (this.paused)
            {
                // Only accept restart or reset
                if ((said.Verb != Verbs.Resume) && (said.Verb != Verbs.Reset))
                {
                    return;
                }

                this.paused = false;
            }
            else
            {
                if (said.Verb == Verbs.Resume)
                {
                    return;
                }
            }

            if (said.Verb == Verbs.Pause)
            {
                this.paused = true;
            }
            
            if (this.SaidSomething != null)
            {
                this.SaidSomething(new object(), said);
            }
        }

        private struct WhatSaid
        {
            public Verbs Verb;
           
        }

        public class SaidSomethingEventArgs : EventArgs
        {
            public Verbs Verb { get; set; }

            public string Phrase { get; set; }

            public string Matched { get; set; }
        }

/*t
        private Choices choicelistchooser()
        {


            
        }
*/
    }
}